package LibDL.data;

import LibDL.recommender.data.utils.NumUtils;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.openjdk.jmh.runner.options.TimeValue;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;

@BenchmarkMode(Mode.AverageTime)
@State(Scope.Thread)
public class NumUtilsBM {

    private List<Integer> list0, list1;
    private Integer[] arr0, arr1;
    private int[] arr2, arr3, arr4;
    private IntStream is0, is1;

    @Param({"1"})
    private int v = 1;
    @Param({"20000000"})
    private int n = 8_000_000;
//    @Param({"1", "2", "4", "2", "1"})
//    private int threadNum;

    @Setup
    public void init() {
        list0 = new ArrayList<>(n);
        list1 = new ArrayList<>(n);
        arr0 = new Integer[n];
        arr1 = new Integer[n];
        arr2 = new int[n];
        arr3 = new int[n];
        arr4 = new int[n];
        for (int i = 0; i < n; i++) {
            list0.add(i);
            list1.add(i);
            arr0[i] = i;
            arr1[i] = i;
            arr2[i] = i;
            arr3[i] = i;
            arr4[i] = i;
        }
        is0 = IntStream.rangeClosed(0, n);
        is1 = IntStream.rangeClosed(0, n);
    }
    @TearDown
    public void check() {
        System.out.println(arr0[0]);
        System.out.println(arr1[0]);
        System.out.println(arr2[0]);
        System.out.println(arr3[0]);
        System.out.println(arr4[0]);
    }

//    @Benchmark
    public List<Integer> listAdd_s() {
        NumUtils.listAdd(list0, v);
        return list0;
    }

    @Benchmark
    public List<Integer> listAdd_p() throws InterruptedException {
        NumUtils.listAdd1(list1, v);
        return list1;
    }

    @Benchmark
    public List<Integer> listAdd_stream() {
        _NumUtils.listAdd_stream(list0, v);
        return list0;
    }
    @Benchmark
    public Integer[] arrAdd() {
        _NumUtils.arrAdd(arr0, n, v);
        return arr0;
    }

//    @Benchmark
    public Integer[] arrAdd1() throws InterruptedException {
        _NumUtils.arrAdd1(arr1, n, v, 4);
        return arr1;
    }

//    @Benchmark
    public int[] arrAdd2() {
        _NumUtils.arrAdd2(arr2, n, v);
        return arr2;
    }

//    @Benchmark
    public IntStream streamAdd() {
        is0 = _NumUtils.streamAdd(is0, v);
        return is0;
    }

//    @Benchmark
    public int[] streamAdd1() {
        return _NumUtils.streamAdd1(is1, v);
    }

    @Benchmark
    public int[] streamAdd_s() {
        arr3 = _NumUtils.streamAdd_s(arr3, v);
        return arr3;
    }

    @Benchmark
    public int[] streamAdd_p() {
        arr4 = _NumUtils.streamAdd_p(arr4, v);
        return arr4;
    }

    public static void main(String[] args) throws RunnerException {
//        otherTest();
        java.lang.Compiler.enable();
//        manualTest();
        int availProcessors = Runtime.getRuntime().availableProcessors();
        System.out.println("# avail processors count: " + availProcessors);
        Options options = new OptionsBuilder()
                .include(NumUtilsBM.class.getSimpleName())
                .timeout(TimeValue.valueOf("30s"))
                .forks(1)
                .warmupIterations(9)
                .warmupTime(TimeValue.valueOf("3s"))
                .measurementIterations(5)
                .measurementTime(TimeValue.valueOf("10s"))
                .build();
        new Runner(options).run();
        java.lang.Compiler.enable();
    }

    private static void manualTest() {
        NumUtilsBM n = new NumUtilsBM();
        n.init();
        long l1 = System.nanoTime();
        n.streamAdd1();
        long l2 = System.nanoTime();
        n.arrAdd();
        long l3 = System.nanoTime();
        System.out.println((l2 - l1) / 1000);
        System.out.println((l3 - l2) / 1000);
        System.exit(0);
    }
    private static void otherTest() {
        Integer[] arr = new Integer[] {1, 2, 3, 4, 5};
        _NumUtils.arrAdd(arr, 5, 1);
        System.out.println(arr[0]);
    }
}
